package org.fujene.memoryblock;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

import org.fujene.structure.Constants;
import org.fujene.structure.LookupPointer;
import org.fujene.toolbox.ByteConverter;

public class SkipPointerBlock extends MemoryBlock{
	private int opt_length = 0;
	private int opt_nextblock = -1;
	private int opt_type = 0;

	public SkipPointerBlock(int bodySize, int ID, boolean isAllocate){
		super(bodySize, ID, Constants.MEMORY_TYPE_SKIPPTR, isAllocate);
	}

	public void setType(int type){
		this.opt_type = type;
	}

	public int getIndexType(){
		return opt_type;
	}

	public void setPointerLast(LookupPointer dictPtr){
		setPointer(-1, dictPtr, false);
	}

	// Skip pointerが複数ブロックにまたがるときに使用
	public byte[] movePointerRep(byte[] moveval){
		if (opt_nextblock != -1 || (opt_nextblock == -1 && this.getLeft() == 0)){
			// 空いてなければ他のブロックに玉突き
			int moveLength = (opt_length - 1) * PTR;
			byte[] lastValArray = getBody(moveLength, PTR);
			moveBody(0, moveLength, PTR);
			setBody(moveval, 0);
			return lastValArray;
		}else{
			moveBody(0, opt_length * PTR, PTR);
			bodyVacancy -= PTR;
			opt_length++;
			setBody(moveval, 0);
			return null;
		}
	}

	public byte[] movePointerFirst(int index, int from, byte[] val){
		if (opt_nextblock != -1 || (opt_nextblock == -1 && this.getLeft() == 0)){
			// 空いてなければ他のブロックに玉突き
			byte[] lastValArray = getBody((opt_length - 1) * PTR, PTR);
			moveBody(from, (opt_length - index - 1) * PTR, PTR);
			setBody(val, from);
			return lastValArray;
		}else{
			moveBody(from, (opt_length - index) * PTR, PTR);
			setBody(val, from);
			bodyVacancy -= PTR;
			opt_length++;
			return null;
		}
	}

	public byte[] setPointer(int index, LookupPointer dictPtr, boolean isModify){
		if (index < opt_length && index >= 0){
			int from = index * PTR;
			if (!isModify){
				// 挿入
				return movePointerFirst(index, from, ByteConverter.setPtr(dictPtr));
			}else{
				if (opt_length == 0){
					bodyVacancy -= PTR;
					opt_length++;
				}
				// 書き換え
				setBody(ByteConverter.setPtr(dictPtr), from);
				return null;
			}
		}else{
			bodyVacancy -= PTR;
			// 追加
			int from = opt_length * PTR;
			opt_length++;
			setBody(ByteConverter.setPtr(dictPtr), from);
			return null;
		}
	}

	public void setNextBlock(int next){
		opt_nextblock = next;
	}

	public int getNextblock(){
		return opt_nextblock;
	}

	public int getLength(){
		return opt_length;
	}

	public LookupPointer getPointer(int index){
		// OutOfBounds
		if (index >= opt_length) return null;
		return ByteConverter.getPtr(getBody(index * PTR, PTR));
	}

	public int getLeft(){
		int left = this.bodyVacancy / PTR;
		return left;
	}

	public static byte[] createByteArray(LookupPointer dictPtr){
		return ByteConverter.setPtr(dictPtr);
	}

	public byte[] getData() throws IOException{
		ByteArrayOutputStream stream = new ByteArrayOutputStream(this.bodySize + 12);

		stream.write(getOptionByteStream());
		stream.write(this.getBody(0, this.bodySize));

		return stream.toByteArray();
	}

	public void setData(byte[] array){
		opt_length = ByteConverter.byteToInt(array, 0, 4);
		opt_nextblock = ByteConverter.byteToInt(array, 4, 4);
		opt_type = ByteConverter.byteToInt(array, 8, 4);

		setBody(array, 12, bodySize, 0);
	}

	@Override
	public int getOptionSize(){
		return 3;
	}

	@Override
	public byte[] getOptionByteStream(){
		byte[] retb = new byte[4 * 3];

		System.arraycopy(ByteConverter.intToByte(opt_length, 4), 0, retb, 0, 4);
		System.arraycopy(ByteConverter.intToByte(opt_nextblock, 4), 0, retb, 4, 4);
		System.arraycopy(ByteConverter.intToByte(opt_type, 4), 0, retb, 8, 4);

		return retb;
	}

	@Override
	public void setOption(int option, int value){
		if (option == 0)
			opt_length = value;
		else if (option == 1)
			opt_nextblock = value;
		else if (option == 2) opt_type = value;
	}

	@Override
	public String getClassName(){
		return new String("SkipPointerBlock for "
				+ (opt_type == Constants.MEMORY_TYPE_SKIPPTR ? "term" : "content"));
	}
}
